<?php
/**
 * 公共函数
 */
declare(strict_types=1);

namespace Libraries;

use ErrorException;
use Throwable;

class Helper
{

    
    /**
     * 获取一个随机字符串
     * @param int $intLen
     * @return string
     */
    public static function getRandStr(int $intLen = 8): string
    {
        $strAll = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $intTLen = strlen($strAll) - 1;
        $strRe = '';
        for ($i = 0; $i < $intLen; $i++) {
            $strRe .= $strAll[mt_rand(0, $intTLen)];
        }
        return $strRe;
    }

    /**
     * 有效强度的密码验证：包含大小写字母、数字、标点符号、至少8位
     * @param string $password
     * @return bool
     */
    public static function passwordVerify(string $password): bool
    {
        $pattern = '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*?[\^\\$.*+\-?=!:|\/()\[\]{}#_~]).{8,}$/';
        return preg_match($pattern, $password) === 1;
    }

    /**
     * 记录服务运行时，抛出的异常
     * @param Throwable $objE
     * @param string $strName
     * @param bool $blnDump 是否Dump整个对象实例
     * @return string
     */
    public static function saveThrowLog(Throwable $objE, string $strName, bool $blnDump = false): string
    {
        $strMsg = '-------------------- ' . date('Y-m-d H:i:s') . ' --------------------' . PHP_EOL;
        if ($blnDump) {
            $strMsg .= var_export($objE, true) . PHP_EOL;
        } else {
            $strMsg .= 'message => ' . $objE->getMessage() . PHP_EOL;
            $strMsg .= 'file => ' . $objE->getFile() . PHP_EOL;
            $strMsg .= 'line => ' . $objE->getLine() . PHP_EOL;
            $strMsg .= 'code => ' . $objE->getCode() . PHP_EOL;
        }
        $strMsg .= PHP_EOL;
        $strFile = CACHES_PATH . '/throw_log/' . date('ymd') . '/' . $strName . '_' . date('H') . '.log';
        File::coWrite($strFile, $strMsg, 'a');
        return $strMsg;
    }
    
    /**
     * 给出异常的HTML
     * @param string $strThrowMsg
     * @return string
     */
    public static function getThrowHtml(string $strThrowMsg): string
    {
        return <<<HTML
<!DOCTYPE html>
<html lang="zh">
    <head>
        <title>出错了</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>出错了 -_-!</h1>
        <xmp>
{$strThrowMsg}
        </xmp>
    </body>
</html>
HTML;
    }

    /**
     * 字符串加密, 提示: php7以上,建议使用 openssl_encrypt() 和 openssl_decrypt()
     * @param string $str
     * @param string $key
     * @param bool $blnDecode
     * @return string
     */
    public static function strEncode(string $str, string $key, bool $blnDecode = false): string
    {
        $str = ($blnDecode ? base64_decode(urldecode($str)) : $str);
        $code = '';
        $keyLen = strlen($key);
        $strLen = strlen($str);
        for ($i = 0; $i < $strLen; $i++) {
            $k = $i % $keyLen; // 余数,将字符全部位移
            $code .= $str[$i] ^ $key[$k]; // 位移
        }
        return ($blnDecode ? $code : urlencode(base64_encode($code)));
    }

    /**
     * 字符串解密
     * @param string $str
     * @param string $key
     * @return string
     */
    public static function strDecode(string $str, string $key): string
    {
        return self::strEncode($str, $key, true);
    }
    
    /**
     * TCP封包
     * @param array $arrData
     * @return string|null
     * @throws ErrorException
     */
    public static function packTcpMsg(array $arrData): ?string
    {
        $strJson = self::jsonEncode($arrData);
        if ($strJson !== false && ($intLen = strlen($strJson)) < 65533) {
            return pack('nA' . $intLen, $intLen, $strJson);
        }
        throw new ErrorException('TCP封包错误，请检查，可能是内容过长！');
    }
    
    /**
     * TCP解包
     * @param string $strMsg
     * @return array
     */
    public static function unpackTcpMsg(string $strMsg): array
    {
        $strMsg = substr($strMsg, 2);
        $arrData = [];
        if ($strMsg) {
            $arrData = self::jsonDecode($strMsg, true);
            $arrData || $arrData = [];
        }
        return $arrData;
    }

    /**
     * Method：
     * json_encode加JSON_FORCE_OBJECT(强制转为对象)
     * JSON_UNESCAPED_SLASHES(不转义反斜杠)
     * JSON_UNESCAPED_UNICODE(中文不转为unicode)
     * @param array $data
     * @return string
     */
    public static function jsonEncode(array $data): string
    {
        return json_encode($data, JSON_FORCE_OBJECT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    }

    /**
     * 更优化的Json-decode
     * @param string $strJson
     * @param bool $blnAssoc
     * @param int $intDepth
     * @param int $intOptions
     * @return mixed
     */
    public static function jsonDecode(string $strJson, bool $blnAssoc = false, int $intDepth = 512, int $intOptions = 0)
    {
        $fun = 'json_decode';
        if (function_exists('json5_decode')) {
            $fun = 'json5_decode';
        }
        return $fun($strJson, $blnAssoc, $intDepth, $intOptions);
    }
}
